package com.xcity.game.io;

import java.io.OutputStream;
import java.util.Arrays;

/**
 * do not throws exceptions<br/>
 * can get the native buf array
 * @author yang.li
 *
 */
public class SerializeStream extends OutputStream {

	private byte[] buf;
	private int size;
	
	public SerializeStream() {
		this(64);
	}
	
	public SerializeStream(int capacity) {
		this.buf = new byte[capacity];
	}
	
	@Override
	public void write(int b)/* throws IOException*/ {
		ensureCapacity(size + 1);
		buf[size++] = (byte) b;
	}
	
	@Override
	public void write(byte[] b)/* throws IOException*/ {
		write(b, 0, b.length);
	}

	@Override
	public void write(byte[] b, int off, int len)/* throws IOException*/ {
		ensureCapacity(size + len);
		System.arraycopy(b, off, buf, size, len);
		size += len;
	}
	
	public void writeBoolean(boolean b) {
		write(b ? 1 : 0);
	}
	
	public void writeChar(char c) {
		ensureCapacity(size + 2);
		buf[size++] = (byte) (c >>> 8 & 0xff);
		buf[size++] = (byte) (c >>> 0 & 0xff);
	}
	
	public void writeShort(short s) {
		ensureCapacity(size + 2);
		buf[size++] = (byte) (s >>> 8 & 0xff);
		buf[size++] = (byte) (s >>> 0 & 0xff);
	}
	
	public void writeInt(int i) {
		ensureCapacity(size + 4);
		buf[size++] = (byte) (i >>> 24 & 0xff);
		buf[size++] = (byte) (i >>> 16 & 0xff);
		buf[size++] = (byte) (i >>> 8 & 0xff);
		buf[size++] = (byte) (i >>> 0 & 0xff);
	}
	
	public void writeLong(long l) {
		ensureCapacity(size + 8);
		buf[size++] = (byte) (l >>> 56 & 0xff);
		buf[size++] = (byte) (l >>> 48 & 0xff);
		buf[size++] = (byte) (l >>> 40 & 0xff);
		buf[size++] = (byte) (l >>> 32 & 0xff);
		buf[size++] = (byte) (l >>> 24 & 0xff);
		buf[size++] = (byte) (l >>> 16 & 0xff);
		buf[size++] = (byte) (l >>> 8 & 0xff);
		buf[size++] = (byte) (l >>> 0 & 0xff);
	}
	
	public void writeFloat(float f) {
		writeInt(Float.floatToIntBits(f));
	}
	
	public void writeDouble(double d) {
		writeLong(Double.doubleToLongBits(d));
	}
	
	public void writeUTF(String str) {
		int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        /* use charAt instead of copying String to char array */
        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        if (utflen > 65535)
            throw new IllegalArgumentException("encoded string too long: " + utflen + " bytes");

        byte[] bytearr = new byte[utflen+2];

        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);

        int i=0;
        for (i=0; i<strlen; i++) {
           c = str.charAt(i);
           if (!((c >= 0x0001) && (c <= 0x007F))) break;
           bytearr[count++] = (byte) c;
        }

        for (;i < strlen; i++){
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }
        write(bytearr, 0, utflen+2);
	}
	
	public void writeEnum(Enum<?> e) {
		writeInt(e.ordinal());
	}
	
	@Override
	public void close()/* throws IOException*/ {
	}
	
	private void ensureCapacity(int newCap) {
		int cap = buf.length;
		if (newCap > cap) {
			cap <<= 1;
			if (cap > newCap) {
				newCap = cap;
			}
			byte[] newBuf = new byte[newCap];
			System.arraycopy(buf, 0, newBuf, 0, buf.length);
			buf = newBuf;
		}
	}

	public void reset() {
		size = 0;
	}
	
	public int size() {
		return size;
	}
	
	public byte[] buf() {
		return buf;
	}
	
	public byte[] toByteArray() {
		return buf.length != size ? Arrays.copyOf(buf, size) : buf;
	}

}
